
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Frame;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.media.opengl.GLCapabilities;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;

import org.j3d.aviatrix3d.ApplicationUpdateObserver;
import org.j3d.aviatrix3d.Group;
import org.j3d.aviatrix3d.Layer;
import org.j3d.aviatrix3d.Shape3D;
import org.j3d.aviatrix3d.SimpleLayer;
import org.j3d.aviatrix3d.SimpleScene;
import org.j3d.aviatrix3d.SimpleViewport;
import org.j3d.aviatrix3d.TransformGroup;
import org.j3d.aviatrix3d.Viewpoint;
import org.j3d.aviatrix3d.management.SingleDisplayCollection;
import org.j3d.aviatrix3d.management.SingleThreadRenderManager;
import org.j3d.aviatrix3d.output.graphics.DebugAWTSurface;
import org.j3d.aviatrix3d.pipeline.graphics.DefaultGraphicsPipeline;
import org.j3d.aviatrix3d.pipeline.graphics.GraphicsCullStage;
import org.j3d.aviatrix3d.pipeline.graphics.GraphicsOutputDevice;
import org.j3d.aviatrix3d.pipeline.graphics.GraphicsSortStage;
import org.j3d.aviatrix3d.pipeline.graphics.NullCullStage;
import org.j3d.aviatrix3d.pipeline.graphics.NullSortStage;
import org.j3d.geom.particle.ColorRampFunction;
import org.j3d.geom.particle.MaxTimeParticleFunction;
import org.j3d.geom.particle.ParticleInitializer;
import org.j3d.geom.particle.ParticleSystemManager;
import org.j3d.geom.particle.PhysicsFunction;
import org.j3d.geom.particle.PolylineEmitter;
import org.j3d.renderer.aviatrix3d.geom.particle.PointParticleSystem;
import org.j3d.renderer.aviatrix3d.geom.particle.TriangleFanParticleSystem;

// Application Specific imports

public class SistemaParticulasTeste extends Frame implements WindowListener,
		ApplicationUpdateObserver, MouseWheelListener {

	private ParticleInitializer emissor;

	/** Manager for the scene graph handling */
	private SingleThreadRenderManager cena;

	/** Manager for the layers etc */
	private SingleDisplayCollection display;

	/** Our drawing surface */
	private GraphicsOutputDevice superficie;

	/** The particle system manager that we want to clock */
	private ParticleSystemManager sistemaParticulas;

	public SistemaParticulasTeste() {

		setLayout(new BorderLayout());
		addWindowListener(this);
		addMouseWheelListener(this);

		setupAviatrix();
		setupSceneGraph();

		setSize(800, 800);
		setLocation(40, 40);

		setVisible(true);
	}

	/**
	 * Setup avaiatrix
	 */
	private void setupAviatrix() {
		// Assemble a simple single-threaded pipeline.
		GLCapabilities caps = new GLCapabilities();
		caps.setDoubleBuffered(true);
		caps.setHardwareAccelerated(true);

		GraphicsCullStage culler = new NullCullStage();
		culler.setOffscreenCheckEnabled(false);

		GraphicsSortStage sorter = new NullSortStage();
		superficie = new DebugAWTSurface(caps);
		DefaultGraphicsPipeline pipeline = new DefaultGraphicsPipeline();

		pipeline.setCuller(culler);
		pipeline.setSorter(sorter);
		pipeline.setGraphicsOutputDevice(superficie);

		display = new SingleDisplayCollection();
		display.addPipeline(pipeline);

		// Render manager
		cena = new SingleThreadRenderManager();
		cena.addDisplay(display);
		cena.setMinimumFrameInterval(100);

		// Before putting the pipeline into run mode, put the canvas on
		// screen first.
		Component comp = (Component) superficie.getSurfaceObject();
		add(comp, BorderLayout.CENTER);
	}

	/**
	 * gerando a cena
	 */
	private void setupSceneGraph() {

		Viewpoint vp = new Viewpoint();

		Vector3f trans = new Vector3f(0, -0.25f, 3);

		Matrix4f mat = new Matrix4f();
		mat.setIdentity();
		mat.setTranslation(trans);

		TransformGroup tx = new TransformGroup();
		tx.addChild(vp);
		tx.setTransform(mat);

		Group scene_root = new Group();
		scene_root.addChild(tx);

		// this will do basic color interpolations over time
		float[] time = { 0, 1.5f, 2.5f, 5 };
		float[] colors = { 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0 };

		int particleCount = 20000;
		float[] direction = { 0, -5, 0 };

		// create the ParticleInitializer for the ParticleSystem
		// the initializer is used to control how long particles within
		// the system live and how they are reinitialized when they die.
		// this simple initializer lets particles live for 200 iterations
		// and moves them to point 0,0,0 when they die
		
		//float[] line = { -1, 1, 0, -0.5f, 1, 0, 0, 1, 0, 0.5f, 1, 0, 1f, 1, 0 };
		float[] line = { -1, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0, 0.5f, 0, 0.5f, 0.5f, 0.5f, 1f, 0.5f, 1.5f };
		
		emissor = new PolylineEmitter(6000, particleCount, line,
				line.length / 3, colors, direction, .1f, .25f);

		emissor.setMass(0.0005f);
		emissor.setSurfaceArea(0.00004f);

		 TriangleFanParticleSystem smoke_system = new
		 TriangleFanParticleSystem(
		 "smoke", particleCount, 4);

		// QuadParticleSystem smoke_system =
		// new QuadParticleSystem("smoke", particleCount, 4);

		//PointParticleSystem smoke_system = new PointParticleSystem("smoke",
			//	particleCount, 4);

		smoke_system.setParticleInitializer(emissor);

		smoke_system.addParticleFunction(new MaxTimeParticleFunction());
		smoke_system.addParticleFunction(new PhysicsFunction());

		ColorRampFunction colorRamp = new ColorRampFunction(time, colors, true);
		smoke_system.addParticleFunction(colorRamp);

		sistemaParticulas = new ParticleSystemManager();
		sistemaParticulas.addParticleSystem(smoke_system);

		// Now create the shape to put the particle system in
		Shape3D p_shape = new Shape3D();
		p_shape.setGeometry(smoke_system.getNode());

		scene_root.addChild(p_shape);

		SimpleScene scene = new SimpleScene();
		scene.setRenderedGeometry(scene_root);
		scene.setActiveView(vp);

		// Then the basic layer and viewport at the top:
		SimpleViewport view = new SimpleViewport();
		view.setDimensions(0, 0, 800, 800);
		view.setScene(scene);

		SimpleLayer layer = new SimpleLayer();
		layer.setViewport(view);

		Layer[] layers = { layer };
		display.setLayers(layers, 1);
		cena.setApplicationObserver(this);
	}

	/**
	 * Notification that now is a good time to update the scene graph. Use it to
	 * clock the particle system.
	 */
	public void updateSceneGraph() {
		sistemaParticulas.update();
	}

	/**
	 * Notification that the AV3D internal shutdown handler has detected a
	 * system-wide shutdown. The aviatrix code has already terminated rendering
	 * at the point this method is called, only the user's system code needs to
	 * terminate before exiting here.
	 */
	public void appShutdown() {
		// do nothing
	}

	// ---------------------------------------------------------------
	// Methods defined by WindowListener
	// ---------------------------------------------------------------

	/**
	 * Ignored
	 */
	public void windowActivated(WindowEvent evt) {
	}

	/**
	 * Ignored
	 */
	public void windowClosed(WindowEvent evt) {
	}

	/**
	 * Exit the application
	 * 
	 * @param evt
	 *            The event that caused this method to be called.
	 */
	public void windowClosing(WindowEvent evt) {
		cena.shutdown();
		System.exit(0);
	}

	/**
	 * Ignored
	 */
	public void windowDeactivated(WindowEvent evt) {
	}

	/**
	 * Ignored
	 */
	public void windowDeiconified(WindowEvent evt) {
	}

	/**
	 * Ignored
	 */
	public void windowIconified(WindowEvent evt) {
	}

	public void windowOpened(WindowEvent evt) {
		cena.setEnabled(true);
	}

	public static void main(String[] args) {
		new SistemaParticulasTeste();
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		if (e.getWheelRotation() > 0 && emissor.getSpeed() < 10)
			emissor.setSpeed(emissor.getSpeed() + 0.5f);

		if (e.getWheelRotation() < 0 && emissor.getSpeed() > 1)
			emissor.setSpeed(emissor.getSpeed() - 0.5f);

		System.out.println(emissor.getSpeed());
	}

}